#undef  xuser
#define xuser mixc::net_private_base_net::inc
#include"macro/xmaybe_unused.hpp"
#include"net/private/base_net.hpp"
#include<arpa/inet.h>
#include<netinet/in.h>
#include<netinet/tcp.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include"memop/copy.hpp"

namespace mixc::net_private_base_net::inc{
    extern void set_read_timeout(ixx fd, u64 millisecond){
        timeval tv{};
        tv.tv_sec = millisecond / 1000;
        tv.tv_usec = millisecond % 1000 * 1000;
        setsockopt(int(fd), SOL_SOCKET, SO_RCVTIMEO, & tv, sizeof(tv));
    }

    extern u64 get_read_timeout(ixx fd){
        timeval tv{};
        socklen_t len{};
        getsockopt(int(fd), SOL_SOCKET, SO_RCVTIMEO, & tv, & len);
        return tv.tv_sec * 1000 + tv.tv_usec / 1000;
    }

    extern void set_write_timeout(ixx fd, u64 millisecond){
        timeval tv{};
        tv.tv_sec = millisecond / 1000;
        tv.tv_usec = millisecond % 1000 * 1000;
        setsockopt(int(fd), SOL_SOCKET, SO_SNDTIMEO, & tv, sizeof(tv));
    }

    extern u64 get_write_timeout(ixx fd){
        timeval tv{};
        socklen_t len{};
        getsockopt(int(fd), SOL_SOCKET, SO_SNDTIMEO, & tv, & len);
        return tv.tv_sec * 1000 + tv.tv_usec / 1000;
    }

    extern inc::sock_type_t get_sock_type(ixx fd){
        auto type       = int{};
        auto addr       = sockaddr_in{};
        auto len_type   = socklen_t(sizeof(type));
        auto len_addr   = socklen_t(sizeof(addr));
        getsockopt(int(fd), SOL_SOCKET, SO_TYPE, & type, & len_type);
        getsockname(int(fd), (sockaddr *)& addr, & len_addr);

        if (type == SOCK_STREAM){
            if (addr.sin_family == AF_INET){
                return inc::sock_type_t::ipv4_stream;
            }
            if (addr.sin_family == AF_INET6){
                return inc::sock_type_t::ipv6_stream;
            }
        }
        else if (type == SOCK_DGRAM){
            if (addr.sin_family == AF_INET){
                return inc::sock_type_t::ipv4_dgram;
            }
            if (addr.sin_family == AF_INET6){
                return inc::sock_type_t::ipv6_dgram;
            }
        }

        return inc::sock_type_t::not_exist;
    }
}

namespace mixc::net_private_base_net::origin{
    bstate_t base_net::open(inc::sock_type_t type, inc::reuse_address_t mode) const {
        if (m_fd > 0){
            this->close();
        }

        switch (type){
        case inc::sock_type_t::ipv4_stream: m_fd = ::socket(AF_INET , SOCK_STREAM,  0); break;
        case inc::sock_type_t::ipv6_stream: m_fd = ::socket(AF_INET6, SOCK_STREAM,  0); break;
        case inc::sock_type_t::ipv4_dgram:  m_fd = ::socket(AF_INET , SOCK_DGRAM,   0); break;
        case inc::sock_type_t::ipv6_dgram:  m_fd = ::socket(AF_INET6, SOCK_DGRAM,   0); break;
        default: return bstate_t::fail;
        }

        if (m_fd < 0){
            return bstate_t::fail;
        }
        else{
            linger lin{};
            lin.l_onoff = 1; // on
            lin.l_linger = 1024; // 1024s 超时，等待发送缓冲区发送完毕
            ::setsockopt(int(m_fd), SOL_SOCKET, SO_LINGER, & lin, sizeof(lin));

            if (mode != inc::reuse_address_t::system_default){
                i32 on = i32(mode);
                ::setsockopt(int(m_fd), SOL_SOCKET, SO_REUSEADDR, & on, sizeof(on));
            }
            return bstate_t::success;
        }
    }

    bstate_t base_net::bind(inc::ipv4 ip, u16 port) const {
        sockaddr_in args{
            .sin_family = (u16)(AF_INET),
            .sin_port = inc::port{}.from_local(port)->net_form,
            .sin_addr = { ip->net_form },
            .sin_zero = {}
        };

        return ::bind(m_fd, (sockaddr *)& args, sizeof(args)) < 0 ? bstate_t::fail : bstate_t::success;
    }

    bstate_t base_net::bind(xmaybe_unused inc::ipv6 ip, u16 port) const {
        sockaddr_in6 args{
            .sin6_family = (u16)(AF_INET6),
            .sin6_port = inc::port{}.from_local(port)->net_form,

            // TODO:=========================================
            .sin6_flowinfo = {},
            .sin6_addr = {},
            .sin6_scope_id = {},
        };

        return ::bind(m_fd, (sockaddr *)& args, sizeof(args)) < 0 ? bstate_t::fail : bstate_t::success;
    }

    bstate_t base_net::open_with_bind(inc::sock_type_t type, inc::ipv4 ip, u16 port, inc::reuse_address_t mode) const {
        if (this->open(type, mode) == bstate_t::fail){
            return bstate_t::fail;
        }
        return this->bind(ip, port);
    }

    bstate_t base_net::open_with_bind(inc::sock_type_t type, inc::ipv6 ip, u16 port, inc::reuse_address_t mode) const {
        if (this->open(type, mode) == bstate_t::fail){
            return bstate_t::fail;
        }
        return this->bind(ip, port);
    }

    bstate_t base_net::close_core(bool forcedly) const {
        if (m_fd <= 0){
            return bstate_t::fail;
        }

        if (forcedly){
            linger lin{};
            ::setsockopt(int(m_fd), SOL_SOCKET, SO_LINGER, & lin, sizeof(lin));
        }

        bstate_t state  = ::close(m_fd) < 0 ? bstate_t::fail : bstate_t::success;
        m_fd            = 0;
        return state;
    }

    bstate_t base_net::close() const {
        return this->close_core(false);
    }

    bstate_t base_net::close_forcedly() const {
        return this->close_core(true);
    }
}
